﻿#include "../exercise.h"
#include <vector>

// 张量即多维数组。连续存储张量即逻辑结构与存储结构一致的张量。
// 通常来说，形状为 [d0, d1, ..., dn] 的张量，第 n 维是 dn 个连续的元素，第 n-1 维是 dn-1 个连续的 dn 个元素，以此类推。
// 张量的步长或跨度指的是张量每个维度上坐标 +1 时，数据指针跨过的范围。
// 因此，一个连续张量，其第 n 维的步长为 1，第 n-1 维的步长为 dn，第 n-2 维的步长为 dn*dn-1，以此类推。
// 例如，一个 2x3x4 张量，其步长为 [12, 4, 1]。

// READ: 类型别名 <https://zh.cppreference.com/w/cpp/language/type_alias>
using udim = unsigned int;

/// @brief 计算连续存储张量的步长
/// @param shape 张量的形状
/// @return 张量每维度的访问步长
// std::vector<udim> strides(std::vector<udim> const &shape) {
//     std::vector<udim> strides(shape.size());
//     // TODO: 完成函数体，根据张量形状计算张量连续存储时的步长。
//     // READ: 逆向迭代器 std::vector::rbegin <https://zh.cppreference.com/w/cpp/container/vector/rbegin>
//     //       使用逆向迭代器可能可以简化代码
    
//     // 从最高维到最低维计算步长
//     udim stride = 1;
//     for (int i = static_cast<int>(shape.size()) - 1; i >= 0; --i) {
//         strides[i] = stride;
//         stride *= shape[i];
//     }

//     return strides;
// }

// 逆向迭代器：
std::vector<udim> strides(std::vector<udim> const &shape) {
    std::vector<udim> strides(shape.size());
    // TODO: 完成函数体，根据张量形状计算张量连续存储时的步长。
    // READ: 逆向迭代器 std::vector::rbegin <https://zh.cppreference.com/w/cpp/container/vector/rbegin>
    //       使用逆向迭代器可能可以简化代码
    
    udim stride = 1;
    auto rit = shape.rbegin();
    auto sit = strides.rbegin();
    
    for (; rit != shape.rend(); ++rit, ++sit) {
        *sit = stride;
        stride *= *rit;
    }
    
    return strides;
}

// ---- 不要修改以下代码 ----
int main(int argc, char **argv) {
    ASSERT((strides({2, 3, 4}) == std::vector<udim>{12, 4, 1}), "Make this assertion pass.");
    ASSERT((strides({3, 4, 5}) == std::vector<udim>{20, 5, 1}), "Make this assertion pass.");
    ASSERT((strides({1, 3, 224, 224}) == std::vector<udim>{150528, 50176, 224, 1}), "Make this assertion pass.");
    ASSERT((strides({7, 1, 1, 1, 5}) == std::vector<udim>{5, 5, 5, 5, 1}), "Make this assertion pass.");
    return 0;
}



/*
vector的逆向迭代器：rbegin

27_strides\main.cpp(24): warning C4267: “初始化”: 从“size_t”转换到“int”，可能丢失数据

error: execv(D:\e\code\xmake_code\training-cpp\build\windows\x64\release\build\windows\x64\release\exercise27.exe ) failed(-1073741819)

计算原理：

从最低维（最内层）开始：最低维的步长总是1
反向累积乘积：每个维度的步长是后续所有维度大小的乘积
数学公式：strides[i] = shape[i+1] * shape[i+2] * ... * shape[n-1]

步长的实际应用
1. 索引计算
给定形状[d0, d1, d2]和步长[s0, s1, s2]，坐标[i, j, k]对应的线性索引为： index = i*s0 + j*s1 + k*s2

2. 内存访问优化
步长信息可以帮助编译器生成更高效的内存访问代码，特别是在循环嵌套中。

3. 张量操作
在深度学习框架中，步长用于实现：

转置操作（交换步长）
切片操作（调整步长）
广播操作（扩展步长）
*/